package com.cacheserverdeploy.algorithm;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Dijkstra最短路径从一个源到节点的最短路径
 * 
 * @author 水电菜鸟 参数说明： cVertexs--起始消费节点（customer vertex）.即计算顶点vs到其他定点的最短路径
 *         prev--前驱顶点数组， dist--长度数组，即dist[i]是顶点vs到顶点i的最短路径
 */
public class SingleSourceDijkstra {
	

	/**返回最短距离
	 * @param weight
	 * @param start
	 * @param end
	 * @return
	 */
	public int[] dijkstra(int[][] weight, int start, int[] end) {
		// 接受一个有向图的权重矩阵，和一个起点编号start（从0编号，顶点存在数组中）
		// 返回一个int[] 数组，表示从start到它的最短路径长度
		int n = weight.length; // 顶点个数
		int[] shortPath = new int[n]; // 保存start到其他各点的最短路径
		List<List<Integer>> pathInfo = new ArrayList<>();
		for (int i = 0; i < n; i++) {
			// path[i] = new String(start + "-->" + i);
			pathInfo.add(new ArrayList<Integer>());
			List<Integer> list = pathInfo.get(i);
			list.add(start);
			list.add(list.size(), i);
		}
		int[] visited = new int[n]; // 标记当前该顶点的最短路径是否已经求出,1表示已求出

		// 初始化，第一个顶点已经求出
		shortPath[start] = 0;
		visited[start] = 1;

		for (int count = 1; count < n; count++) { // 要加入n-1个顶点
			int k = -1; // 选出一个距离初始顶点start最近的未标记顶点
			int dmin = Integer.MAX_VALUE;
			for (int i = 0; i < n; i++) {
				if (visited[i] == 0 && weight[start][i] < dmin) {
					dmin = weight[start][i];
					k = i;
				}
			}

			// 将新选出的顶点标记为已求出最短路径，且到start的最短路径就是dmin
			shortPath[k] = dmin;
			visited[k] = 1;
			int sum = 0;
		    for(int i=0;i<end.length;i++)
		    {
		    	sum+=visited[end[i]];
		    }
		    
			if(sum==end.length)
		    {
				break;
			}
		    
			// 以k为中间点，修正从start到未访问各点的距离
			for (int i = 0; i < n; i++) {
				if (visited[i] == 0
					&& weight[start][k] + weight[k][i] < weight[start][i]) {
					weight[start][i] = weight[start][k] + weight[k][i];
					// path[i] = path[k] + "-->" + i;
					List<Integer> list = new ArrayList<Integer>();
					for (Integer I : pathInfo.get(k)) {
						list.add(I);
					}
					list.add(i);
					pathInfo.set(i, list);
				}
				
			}
			
		}
		for(int i=0;i<end.length;i++)
		{
			System.out.println("从" + start + "出发到" + end[i] + "的最短路径为："
					+ pathInfo.get(end[i]).toString());
		}
		System.out.println("=====================================");
		return shortPath;
	}
	
	/**返回路径，只返回到消费点
	 * @param weight
	 * @param start
	 * @param end
	 * @return
	 */
	public List<List<Integer>> dijkstra_Path(int[][] weight, int start, int[] end) {
		// 接受一个有向图的权重矩阵，和一个起点编号start（从0编号，顶点存在数组中）
		// 返回一个int[] 数组，表示从start到它的最短路径长度
		int n = weight.length; // 顶点个数
		int[] shortPath = new int[n]; // 保存start到其他各点的最短路径
		List<List<Integer>> pathInfo = new ArrayList<>();//全部的
		List<List<Integer>> pathInfo1 = new ArrayList<>();//需要的到消费点的
		for (int i = 0; i < n; i++) {
			// path[i] = new String(start + "-->" + i);
			pathInfo.add(new ArrayList<Integer>());
			List<Integer> list = pathInfo.get(i);
			list.add(start);
			list.add(list.size(), i);
		}
		int[] visited = new int[n]; // 标记当前该顶点的最短路径是否已经求出,1表示已求出

		// 初始化，第一个顶点已经求出
		shortPath[start] = 0;
		visited[start] = 1;

		for (int count = 1; count < n; count++) { // 要加入n-1个顶点
			int k = -1; // 选出一个距离初始顶点start最近的未标记顶点
			int dmin = Integer.MAX_VALUE;
			for (int i = 0; i < n; i++) {
				if (visited[i] == 0 && weight[start][i] < dmin) {
					dmin = weight[start][i];
					k = i;
				}
			}

			// 将新选出的顶点标记为已求出最短路径，且到start的最短路径就是dmin
			shortPath[k] = dmin;
			visited[k] = 1;
			int sum = 0;
		    for(int i=0;i<end.length;i++)
		    {
		    	sum+=visited[end[i]];
		    }
		    
			if(sum==end.length)
		    {
				break;
			}
		    
			// 以k为中间点，修正从start到未访问各点的距离
			for (int i = 0; i < n; i++) {
				if (visited[i] == 0
					&& weight[start][k] + weight[k][i] < weight[start][i]) {
					weight[start][i] = weight[start][k] + weight[k][i];
					// path[i] = path[k] + "-->" + i;
					List<Integer> list = new ArrayList<Integer>();
					for (Integer I : pathInfo.get(k)) {
						list.add(I);
					}
					list.add(i);
					pathInfo.set(i, list);
				}
				
			}
			
		}
		
		for(int i=0;i<end.length;i++)
		{			
			pathInfo.get(end[i]).add(shortPath[end[i]]);//后面跟的是最短距离
//			System.out.println("从" + start + "出发到" + end[i] + "的最短路径和最短距离为："
//					+ pathInfo.get(end[i]).toString());
			pathInfo1.add(pathInfo.get(end[i]));
		}
		
		
//		System.out.println("=====================================");
		return pathInfo1;
	}



	
	
	/**
	 * 获取消费点到最近的消费点
	 * 
	 * @param shortPath
	 * @return
	 */
	public List<Integer> findMinPath(List<List<Integer>> shortPath) {
		List<Integer> path = new ArrayList<>();
		int cost = 1000000;
		for (List<Integer> shortestPath : shortPath) {
//			if (shortestPath.get(shortestPath.size() - 1) < cost && shortestPath.get(shortestPath.size() - 1) != 0) {
				if (shortestPath.get(shortestPath.size() - 1) < cost ) {
				cost = shortestPath.get(shortestPath.size() - 1);
				path = shortestPath;
			}
		}
		return path;
	}
	
		
	/**
	 * 找出出现次数最多的,排除id
	 * 
	 * @param paths
	 * @param id
	 * @return
	 */
	public int findMaxNode(List<List<Integer>> paths) {
		List<Integer> node = new ArrayList<>();// 存储出现的点号
		for (List<Integer> path : paths) {
			for (int i = 0; i < path.size(); i++) {
				if (i != (path.size() - 1)) {// 最后一位是距离，去除
					node.add(path.get(i));
				}
			}
		}
		return findMaxAppear(node);

	}

	/**
	 * 找出里面出现次数最多的
	 * 
	 * @param node
	 * @return
	 */
	private int findMaxAppear(List<Integer> array) {
		// map的key存放数组中存在的数字，value存放该数字在数组中出现的次数
		HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();

		for (int i = 0; i < array.size(); i++) {
			if (map.containsKey(array.get(i))) {
				int temp = map.get(array.get(i));

				map.put(array.get(i), temp + 1);
			} else {
				map.put(array.get(i), 1);
			}
		}

		Collection<Integer> count = map.values();

		// 找出map的value中最大的数字，也就是数组中数字出现最多的次数
		int maxCount = Collections.max(count);

		int maxNumber = 0;

		for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
			// 得到value为maxCount的key，也就是数组中出现次数最多的数字
			if (maxCount == entry.getValue()) {
				maxNumber = entry.getKey();
			}
		}

		return maxNumber;
	}

}
